ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'test/rails'
require 'test_help'

class Test::Unit::TestCase
  # Transactional fixtures accelerate your tests by wrapping each test method
  # in a transaction that's rolled back on completion.  This ensures that the
  # test database remains unchanged so your fixtures don't have to be reloaded
  # between every test method.  Fewer database queries means faster tests.
  #
  # Read Mike Clark's excellent walkthrough at
  #   http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
  #
  # Every Active Record database supports transactions except MyISAM tables
  # in MySQL.  Turn off transactional fixtures in this case; however, if you
  # don't care one way or the other, switching from MyISAM to InnoDB tables
  # is recommended.
  self.use_transactional_fixtures = true

  # Instantiated fixtures are slow, but give you @david where otherwise you
  # would need people(:david).  If you don't want to migrate your existing
  # test cases which use the @david style and don't mind the speed hit (each
  # instantiated fixtures translates to a database query per test method),
  # then set this back to true.
  self.use_instantiated_fixtures  = false

  # Add more helper methods to be used by all tests here...
  
  ### EXTRA ASSERTIONS
    
  # Test for the presence of a form tag
  def assert_form_tag(action)
    assert_tag "form", :attributes => { :action => action, :method => "post" }
  end

  # Test for the presence of a particular input tag
  def assert_input_tag(name, type, size = nil, maxlength = nil, value = nil, options = {})
    attributes = { :name => name,
                   :type => type }
    attributes[:size] = size unless size.nil?
    attributes[:maxlength] = maxlength unless maxlength.nil?
    attributes[:value] = value unless value.nil?
    tag = { :tag => "input", :attributes => attributes }
    tag.merge!(options)
    assert_tag tag
  end
  
  # Test for the presence of a link tag 
  def assert_link_tag(content, href, options = {})
    attributes = { :href => href }
    tag = { :tag => "a", :content => content, :attributes => attributes }
    tag.merge!(options)
    assert_tag tag
  end

  def assert_submit_tag(label = nil)
    if label
      assert_tag "input", :attributes => { :type => "submit", :value => label }
    else
      assert_tag "input", :attributes => { :type => "submit" }
    end
  end

  # Check minimum length
  def assert_minimum_length(args = {})
    assert_length( {:boundary => :min}.merge(args) )    
  end

  # Check maximum length
  def assert_maximum_length(args = {})
    assert_length( {:boundary => :max}.merge(args) ) 
  end
  
  # Actually handle the length check
  def assert_length(args = {})
    object = args[:object] || nil
    attribute = args[:attribute] || nil
    length = args[:length] || nil
    boundary = args[:boundary] || nil
    valid_char = args[:valid_char] || "a"
    valid_to_prepend = args[:valid_to_prepend] || ""
    valid_to_append = args[:valid_to_append] || ""
    
    
    # Generate the invalid error message
    if boundary == :max
      error_message = sprintf(@error_messages[:too_long], length)
    elsif boundary == :min
      error_message = sprintf(@error_messages[:too_short], length)
    else
      raise ArgumentError, "boundary must be :max or :min"      
    end
    
    valid = get_valid_length(length, boundary, valid_char, valid_to_prepend, valid_to_append)
    invalid = get_invalid_length(length, boundary, valid_char, valid_to_prepend, valid_to_append)
    
    object[attribute] = valid
    assert object.valid?, 
           "#{object[attribute]} (length #{object[attribute].length}) " +
           "should be a barely acceptable length " +
           object.inspect
    
    object[attribute] = invalid
    assert !object.valid?,
           "#{object[attribute]} (length #{object[attribute].length}) " +
           "should be a barely wrong length."
    assert_equal error_message, object.errors.on(attribute)       

  end

  # get a string just long enough
  def get_valid_length(length, boundary, valid_char = "a", valid_to_prepend = "", valid_to_append = "")  
    initial_length = valid_to_prepend.length + valid_to_append.length
    valid_chunk = valid_char * (length - initial_length)
    valid = valid_to_prepend + valid_chunk + valid_to_append
  end
  
  # get a string just out of bounds
  def get_invalid_length(length, boundary, valid_char = "a", valid_to_prepend = "", valid_to_append = "")  
    initial_length = valid_to_prepend.length + valid_to_append.length
    if boundary == :max
      invalid_chunk = valid_char * ( (length-initial_length) + 1 )
    elsif boundary == :min
      invalid_chunk = valid_char * ( (length-initial_length) - 1 )
    else
      raise ArgumentError, "boundary must be :max or :min"      
    end
    invalid = valid_to_prepend + invalid_chunk + valid_to_append
  end

  # test multiple pages for proper authorized access
  def assert_working_authorization(pages, unauthorized_users, authorized_users, options = {})
    redirect_controller = options[:redirect_controller] ? options[:redirect_controller] : "user"
    redirect_action = options[:redirect_action] ? options[:redirect_action] : "index"
    notice = options[:notice] ? options[:notice] : nil
    template = options[:template] ? options[:template] : nil
    
    pages.each do |page|
      assert_no_not_logged_in_access( page )  # if there's authorization you have to be logged in
      unauthorized_users.each do |user|
        assert_no_unauthorized_access( page, user, redirect_controller, redirect_action, notice )
      end
      authorized_users.each do |user|
        assert_authorized_access( page, user, template )
      end
    end
    
  end
    
  # test a protected page for access by an authorized user
  def assert_authorized_access(path, authorized_user, template = nil)
    @authorized_user = users(authorized_user)
    authorize @authorized_user
    get path
    assert_response :success
    if !template.nil?
      assert_template template
    end
  end

  # test a protected page against access by an unauthorized user  
  def assert_no_unauthorized_access(path, unauthorized_user, redirect_controller, redirect_action, notice = nil)  
    @unauthorized_user = users(unauthorized_user)
    authorize @unauthorized_user
    get path
    assert_response :redirect
    assert_redirected_to :controller => redirect_controller, :action => redirect_action
    if !notice.nil?
      assert_equal notice, flash[:notice]
    end
  end
  
  # test a page against access by a non-logged-in user
  def assert_no_not_logged_in_access(path)
    unauthorize
    get path
    assert_response :redirect
    assert_redirected_to :controller => "user", :action => "login"
    assert_equal "Please log in first", flash[:notice]    
  end

  # Check for particular tag with content in a page, for a given user
  def assert_tag_content(path, user, tag, content)
    @user = users(user)
    authorize @user
    get path
    assert_tag tag, { :content => content }
  end
  
  ### OTHER HELPERS

  # Return cookie value, given a symbol
  def cookie_value(symbol)
    cookies[symbol.to_s].value.first
  end
  
  # Return cookie expiration, given a symbol
  def cookie_expires(symbol)
    cookies[symbol.to_s].expires
  end 

  # Authorize a user for testing
  def authorize(user)
    @request.session[:user_id] = user.id
  end
  
  def unauthorize
    @request.session[:user_id] = nil
  end

  # Try to create a user (with a set of valid options which can be overridden)
  def create_user(options = {})
    User.create({ :username => 'tester', :email => 'tester@example.com', :password => 'testing' }.merge(options))
  end
  
  
end
